package com.lsb.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lsb.filter.MyLoginFilter;
import com.lsb.handler.*;
import com.lsb.mapper.SubmitMapper;
import com.lsb.mapper.UserMapper;
import com.lsb.pojo.Submit;
import com.lsb.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsUtils;

import java.util.List;


@Configuration
//开启方法级别的权限注解驱动
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    //此CommandLineRunner用于使项目启动时就构建当前所有用户当天的打卡记录
/*    @Bean
    public CommandLineRunner commandLineRunner(UserMapper userMapper, SubmitMapper submitMapper){
        return args -> {
            System.out.println("=============CommandLineStart==========");
            List<User> userList = userMapper.selectList(new QueryWrapper<>());
            userList.forEach(user -> {
                //构建一个submit记录
                Submit submit = new Submit(user.getId(),user.getUsername(),
                        user.getRealName(),user.getTelephoneNumber(),user.getIdentityNumber());
                submitMapper.insert(submit);
            });

            System.out.println("=============CommandLineEnd===========");
        };
    }*/

    /**
     * 密码加密处理类
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 自定义用户登录接口
     */
    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 登录成功处理器
     */
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    /**
     * 登录失败处理器
     */
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    /**
     * 未登录，或者登录状态过期失效 访问资源 处理器
     */
    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;

    /**
     * 已登录，访问资源权限不足处理方案
     */
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    /**
     * 注销操作处理器
     */
    @Autowired
    private MyLogoutHandler myLogoutHandler;

    /**
     * 注销成功处理器
     */
    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;

    /**
     * session过期(超时)处理方案
     */
    @Autowired
    private MyInvalidSessionStrategy myInvalidSessionStrategy;

    /**
     * 账号被挤下线的处理方案
     */
    @Autowired
    private MyExpiredSessionStrategy myExpiredSessionStrategy;


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //login接口的登录流程使用UserDetailsServiceImpl，表单明文密码的慢速哈希加密比对使用BCryptPasswordEncoder
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //第1步：解决跨域问题。cors 预检请求放行,让Spring security 放行所有preflight request（cors 预检请求）
        http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();

        //所有的访问请求都要被 认证/登录
        http.authorizeRequests()
                //放行登录接口和注册接口
                .antMatchers("/login","/user/register","/user/modifyPersonalInformation").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .logout().permitAll()
                //注销处理方案(设置客户端Header中的token为空串)
                .addLogoutHandler(myLogoutHandler)
                //注销成功处理方案(给前端回显json处理结果)
                .logoutSuccessHandler(myLogoutSuccessHandler)
                //登出之后删除cookie中的内容(不使用session后可以不管cookie了)
                .deleteCookies("JSESSIONID")
                .and()
                // 设置表单登陆相关配置
                .formLogin()
                //登录表单form中action的地址，也就是处理认证请求的路径
                .loginProcessingUrl("/login")
                //登录表单form中用户名输入框input的name名，不修改的话默认是username
                .usernameParameter("username")
                //登录表单form中密码输入框input的name名，不修改的话默认是password
                .passwordParameter("password")
                //登录成功处理方案
                .successHandler(myAuthenticationSuccessHandler)
                //登录失败处理方案
                .failureHandler(myAuthenticationFailureHandler)
                .and()
                .exceptionHandling()
                //未登录访问资源的处理提示
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                //已登录，访问资源权限不足的处理提示
                .accessDeniedHandler(myAccessDeniedHandler)
                .and()
                //自定义登录认证过滤器，前后端分离从requestBody拿参数，就要自定义一个UsernamePasswordAuthenticationFilter
                .addFilter(new MyLoginFilter(authenticationManager()))
                //会话管理 (session管理)
                .sessionManagement()
                //使用session
                .sessionCreationPolicy(SessionCreationPolicy.ALWAYS);


        //关闭CSRF跨域
        //详解什么是CSRF:https://www.cnblogs.com/pengdai/p/12164754.html
        http.csrf().disable();
    }
}
